#define DEBUG

/*
 * Encryption sample
 * rhoward 4/22/01
 * 
 * This sample encrypts data from an ASP.NET Web Service
 * by only encrypting the values with the actual response
 * of a method call (rather than all the data in the soap message)
 * 
 * This technique allows for the message to be secure, but
 * the soap to still be valid 
 */

using System;
using System.IO;
using System.Xml;
using System.Text;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Security.Cryptography;

public class EncryptionExtension : SoapExtension { 
	Stream oldStream;
	Stream newStream;
	DecryptMode decryptMode;
	EncryptMode encryptMode;
	Target target;
    
	private Byte[] key = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};
	private Byte[] IV = {0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef};

	public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute) {
		return attribute;
	}

	public override object GetInitializer(Type t) {
		return typeof(EncryptionExtension);
	}

	public override void Initialize(object initializer) {
		EncryptionExtensionAttribute attribute = (EncryptionExtensionAttribute) initializer;

		// Find the mode we should be in
		decryptMode = attribute.Decrypt;
		encryptMode = attribute.Encrypt;
		target = attribute.Target;
		return;
	}

	public override void ProcessMessage(SoapMessage message) {
		switch (message.Stage) {

			case SoapMessageStage.BeforeSerialize:
				break;

			case SoapMessageStage.AfterSerialize:
				Encrypt();
				break;

			case SoapMessageStage.BeforeDeserialize:
				Decrypt();
				break;

			case SoapMessageStage.AfterDeserialize:
				break;

			default:
				throw new Exception("invalid stage");
		}
	}

	public override Stream ChainStream( Stream stream ) {
		oldStream = stream;
		newStream = new MemoryStream();
		return newStream;
	}
 
	private void Decrypt() {
		MemoryStream decryptedStream = new MemoryStream();
		
		if ((decryptMode == DecryptMode.Request) || (decryptMode == DecryptMode.Response)){
			TextReader reader = new StreamReader(oldStream);
			TextWriter writer = new StreamWriter(decryptedStream);
			writer.WriteLine(reader.ReadToEnd());
			writer.Flush();

			decryptedStream = DecryptSoap(decryptedStream);

			Copy(decryptedStream, newStream);
		} else {
			Copy(oldStream, newStream);
		}

		newStream.Position = 0;
	}

	private void Encrypt() {
		newStream.Position = 0;

		if ((encryptMode == EncryptMode.Request) || (encryptMode == EncryptMode.Response))
			newStream = EncryptSoap(newStream);

		Copy(newStream, oldStream);
	}

	private byte[] CovertStringToByteArray(string s) {
		char[] c = {' '};
		string[] ss = s.Split(c);

		byte[] b = new byte[ss.Length];
    
		for(int i=0; i<b.Length; i++) {
			b[i] = Byte.Parse(ss[i]);
		}

		return b;
	}
	
	private byte[] Decrypt(string stringToDecrypt) {
		DESCryptoServiceProvider des = new DESCryptoServiceProvider();
		byte[] inputByteArray = CovertStringToByteArray(stringToDecrypt);

		MemoryStream ms = new MemoryStream();
		CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor( key, IV ), CryptoStreamMode.Write);

		cs.Write(inputByteArray, 0, inputByteArray.Length);
		cs.FlushFinalBlock();

		return ms.ToArray();
	}

	private byte[] Encrypt(string stringToEncrypt) {
		DESCryptoServiceProvider des = new DESCryptoServiceProvider();

		byte[] inputByteArray = Encoding.UTF8.GetBytes(stringToEncrypt);

		MemoryStream ms = new MemoryStream();
		CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor( key, IV ), CryptoStreamMode.Write);

		cs.Write(inputByteArray, 0, inputByteArray.Length);
		cs.FlushFinalBlock();

		return ms.ToArray();
	}

	public MemoryStream EncryptSoap(Stream streamToEncrypt) {
		streamToEncrypt.Position = 0;
		XmlTextReader reader = new XmlTextReader(streamToEncrypt);
		XmlDocument dom = new XmlDocument();
		dom.Load(reader);

		XmlNamespaceManager nsmgr = new XmlNamespaceManager(dom.NameTable);
		nsmgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
		XmlNode node = dom.SelectSingleNode("//soap:Body", nsmgr);
		node = node.FirstChild.FirstChild;

		byte[] outData = Encrypt(node.InnerText);

		StringBuilder s = new StringBuilder();

		for(int i=0; i<outData.Length; i++) {
			if(i==(outData.Length-1))
				s.Append(outData[i]);
			else
				s.Append(outData[i] + " ");
		}

		node.InnerText = s.ToString();

		MemoryStream ms = new MemoryStream();
		dom.Save(ms);
		ms.Position = 0;

		return ms;
	}

	public MemoryStream DecryptSoap(Stream streamToDecrypt) {
		streamToDecrypt.Position = 0;
		XmlTextReader reader = new XmlTextReader(streamToDecrypt);
		XmlDocument dom = new XmlDocument();
		dom.Load(reader);

		XmlNamespaceManager nsmgr = new XmlNamespaceManager(dom.NameTable);
		nsmgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
		XmlNode node = dom.SelectSingleNode("//soap:Body", nsmgr);
		node = node.FirstChild.FirstChild;

		byte[] outData = Decrypt(node.InnerText);

		string sTmp = Encoding.UTF8.GetString(outData);

		node.InnerText = sTmp;

		MemoryStream ms = new MemoryStream();
		ms.Position = 0;
		dom.Save(ms);
		ms.Position = 0;

		return ms;
	}
	
	void Copy(Stream from, Stream to) {
		TextReader reader = new StreamReader(from);
		TextWriter writer = new StreamWriter(to);
		writer.WriteLine(reader.ReadToEnd());
		writer.Flush();
	}
}